{
 "cells": [
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": [
    "# 轮廓检测\n",
    "#### cv2.findContours(img, mode, method)\n",
    "mode: 轮廓的检索模式\n",
    "- RETR_EXTERNAL:只检测最外轮廓\n",
    "- RETR_LIST:检测所有轮廓,并将其保存到一个链表中\n",
    "- RETR_CCOMP:检测所有轮廓,并将他们组织为两层：顶层是各部分的外部边界，第二层是空洞的边界\n",
    "- RETR_TREE:检测所有轮廓,并重构嵌套轮廓的整个层次\n",
    "\n",
    "method: 轮廓的 Approximation 方法\n",
    "- CHAIN_APPROX_NONE:以Freeman链码的方式输出轮廓，所有其他方法输出多边形(顶点的序列)\n",
    "- CHAIN_APPROX_SIMPLE:压缩水平、垂直和斜线段，只保留他们的终点\n",
    "\n",
    "![title](data/chain.png)"
   ],
   "id": "3fa9ed907299ae69"
  },
  {
   "metadata": {
    "collapsed": true,
    "ExecuteTime": {
     "end_time": "2025-09-16T09:08:56.744190Z",
     "start_time": "2025-09-16T09:08:56.715133Z"
    }
   },
   "cell_type": "code",
   "source": [
    "import cv2\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "def cv_show(img, name):\n",
    "    cv2.imshow(name, img)\n",
    "    cv2.waitKey(0)\n",
    "    cv2.destroyAllWindows()\n",
    "\n",
    "img = cv2.imread(r'../data/contours.png', cv2.IMREAD_GRAYSCALE)\n",
    "# cv_show(img, 'img')\n",
    "ret, thresh = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)\n",
    "# cv_show(thresh, 'thresh')\n",
    "contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)"
   ],
   "id": "initial_id",
   "outputs": [],
   "execution_count": 20
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "绘制轮廓",
   "id": "c3ea1445fccc4924"
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-09-16T09:08:59.184255Z",
     "start_time": "2025-09-16T09:08:58.179323Z"
    }
   },
   "cell_type": "code",
   "source": "cv_show(img, 'img')",
   "id": "b9ffc43bae320d4f",
   "outputs": [],
   "execution_count": 21
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-09-16T09:09:00.720723Z",
     "start_time": "2025-09-16T09:08:59.974967Z"
    }
   },
   "cell_type": "code",
   "source": [
    "draw_img = img.copy()\n",
    "res = cv2.drawContours(draw_img, contours, -1, (0, 0, 255), 2)\n",
    "cv_show(res, 'res')"
   ],
   "id": "a7851ccaa074432c",
   "outputs": [],
   "execution_count": 22
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-09-16T09:09:05.214292Z",
     "start_time": "2025-09-16T09:09:01.437190Z"
    }
   },
   "cell_type": "code",
   "source": [
    "draw_img = img.copy()\n",
    "res = cv2.drawContours(draw_img, contours, 0, (0, 0, 255), 2)\n",
    "cv_show(res,'res')"
   ],
   "id": "abee25092897fb5c",
   "outputs": [],
   "execution_count": 23
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "轮廓特征",
   "id": "efa5b3ea21f3f0"
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-09-16T09:09:10.246910Z",
     "start_time": "2025-09-16T09:09:10.227369Z"
    }
   },
   "cell_type": "code",
   "source": [
    "cnt = contours[0]\n",
    "# 面积\n",
    "cv2.contourArea(cnt)"
   ],
   "id": "18e3253a4d2aebe9",
   "outputs": [
    {
     "data": {
      "text/plain": [
       "8500.5"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "execution_count": 24
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-09-16T09:09:11.571885Z",
     "start_time": "2025-09-16T09:09:11.548078Z"
    }
   },
   "cell_type": "code",
   "source": [
    "# 周长\n",
    "cv2.arcLength(cnt, True)"
   ],
   "id": "685b3f0d1224f817",
   "outputs": [
    {
     "data": {
      "text/plain": [
       "437.9482728242874"
      ]
     },
     "execution_count": 25,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "execution_count": 25
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": [
    "轮廓近似\n",
    "![title](data/contours3.png)"
   ],
   "id": "5250810f3d82ffd2"
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-09-16T09:35:47.032929Z",
     "start_time": "2025-09-16T09:35:45.060747Z"
    }
   },
   "cell_type": "code",
   "source": [
    "img = cv2.imread(r'../data/contours2.png')\n",
    "gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)\n",
    "ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)\n",
    "contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)\n",
    "cnt = contours[0]\n",
    "\n",
    "draw_img = img.copy()\n",
    "res = cv2.drawContours(draw_img, [cnt], -1, (0, 0, 255), 2)\n",
    "cv_show(res, 'res')"
   ],
   "id": "6de0496f61cc38ae",
   "outputs": [],
   "execution_count": 38
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-09-16T09:37:36.828883Z",
     "start_time": "2025-09-16T09:37:35.151122Z"
    }
   },
   "cell_type": "code",
   "source": [
    "epsilon = 0.1 * cv2.arcLength(cnt, True)\n",
    "approx = cv2.approxPolyDP(cnt, epsilon, True)\n",
    "\n",
    "draw_img = img.copy()\n",
    "res = cv2.drawContours(draw_img, [approx], -1, (0, 0, 255), 2)\n",
    "cv_show(res, 'res')"
   ],
   "id": "6fa9939a39f18205",
   "outputs": [],
   "execution_count": 41
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "边界矩阵",
   "id": "a31b66c1302e23e"
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-09-16T09:48:50.216493Z",
     "start_time": "2025-09-16T09:48:45.753684Z"
    }
   },
   "cell_type": "code",
   "source": [
    "img = cv2.imread(r'../data/contours.png')\n",
    "\n",
    "gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)\n",
    "ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)\n",
    "contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)\n",
    "cnt = contours[0]\n",
    "\n",
    "x, y, w, h = cv2.boundingRect(cnt)\n",
    "img = cv2.rectangle(img, (x, y), (x+w, y+h), (0, 255, 0), 2)\n",
    "cv_show(img, 'img')"
   ],
   "id": "f7f18833263dbf3",
   "outputs": [],
   "execution_count": 42
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-09-16T09:53:29.961636Z",
     "start_time": "2025-09-16T09:53:29.947208Z"
    }
   },
   "cell_type": "code",
   "source": [
    "area = cv2.contourArea(cnt)\n",
    "x, y, w, h = cv2.boundingRect(cnt)\n",
    "rect_area = w * h\n",
    "extent = float(area) / rect_area\n",
    "print(\"轮廓面积：%f, 边界矩阵面积：%f, 轮廓面积占比：%f\" % (area, rect_area, extent))"
   ],
   "id": "f3f78874f063f0d7",
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "轮廓面积：8500.500000, 边界矩阵面积：16492.000000, 轮廓面积占比：0.515432\n"
     ]
    }
   ],
   "execution_count": 44
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "外接圆",
   "id": "defdfb56a92ceb1c"
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-09-16T09:54:18.559425Z",
     "start_time": "2025-09-16T09:54:15.555192Z"
    }
   },
   "cell_type": "code",
   "source": [
    "(x, y), radius = cv2.minEnclosingCircle(cnt)\n",
    "center = (int(x), int(y))\n",
    "radius = int(radius)\n",
    "img = cv2.circle(img, center, radius, (0, 255, 0), 2)\n",
    "cv_show(img, 'img')"
   ],
   "id": "dbf50f3cb000479b",
   "outputs": [],
   "execution_count": 45
  },
  {
   "metadata": {},
   "cell_type": "code",
   "outputs": [],
   "execution_count": null,
   "source": "",
   "id": "6512133e7bfb282a"
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
